#include "selectiontool.h"
#define IS_SUCCESSFUL true

SelectionTool::SelectionTool()
{
    //Setting properties
    global=GlobalStates::getInstance();
    mode = SCALE;
    action = ADD;
    axesToFix = NO_AXIS;
    isDragging = false;
    showBox = false;
    prevState = global->getSelection();


}



SelectionTool * SelectionTool::getInstance(){
    static SelectionTool* inst=0;
    if(!inst)inst = new SelectionTool();
    inst->global = GlobalStates::getInstance();
    inst->action = ADD;
    inst->showBox = false;
    return inst;
}


string SelectionTool::getType() {
    return "select";
}

Result SelectionTool::setStartCoords(vector3<int> s){
//    if (endCoords > s) {
    if (endCoords > s && GlobalStates::isInAcceptableRange(s)) {
        startCoords = s;
        return Result(true, "Start coordinates set");
    }
    else return Result(false, "Invalid coordinates for start value");
}

Result SelectionTool::setEndCoords(vector3<int> e){
//    if (e > startCoords) {
    if (e > startCoords && GlobalStates::isInAcceptableRange(e)) {
        endCoords = e;
        return Result(true, "End coordinates set");
    }
    else return Result(false, "Invalid coordinates for end value");
}


Result SelectionTool::setAction(ACTION a){
    action = a;
    string msg = "Action has been set to ";
    if (a==ADD || a==EDIT_ADD)
        msg = msg.append("add");
    else msg = msg.append("subtract");
    return Result(true, msg);
}

Result SelectionTool::setMode(MODE m){
    mode = m;
    string msg= "You are now in ";
    if (m == SCALE)
        msg = msg.append("scale ");
    else msg = msg.append("translate ");
    msg= msg.append("mode.");
    return Result(true, msg);
}

int SelectionTool::getMode(){
    return mode;
}

int SelectionTool::getAction(){
    return action;
}

bool SelectionTool::isAxisFixed(int axis){
    return axesToFix & axis;
}



void SelectionTool::setAxis(int axis, bool toFix){
    if (toFix) {
        axesToFix |= axis;
    }
    else {
        axesToFix &= ~axis;
    }
}





Result SelectionTool::operationHandler(vector3<int> coords){
    Result r(false);
    if (mode==SCALE) {
        vector3 <int> centre = startCoords + (endCoords - startCoords)/2;
        float a = (referenceCoords - centre).mag();
        float b = (coords - centre).mag();
        float scaleFactor = (coords - referenceCoords).mag();
        int x = 0, y = 0, z=0;

        if (!isAxisFixed(XAXIS)) {
            x = scaleFactor;
        }
        if (!isAxisFixed(YAXIS)) {
            y = scaleFactor;
        }
        if (!isAxisFixed(ZAXIS)) {
            z = scaleFactor;
        }

        vector3<int> distance = vector3 <int> (x, y, z);
        if (b>a){
            if (setEndCoords(endCoords + distance).getResult()){
                    r= setStartCoords(startCoords - distance);
                    r.setMessage("Box expanded");
            }
        }
        else if (a>b){
            if (setEndCoords(endCoords - distance).getResult()){
            r = setStartCoords(startCoords + distance);
            r.setMessage("Box shrunk");
            }
        }
        referenceCoords = coords;
    }
    else {
        vector3<int> distance = ((coords - referenceCoords));
        if (isAxisFixed(XAXIS)) {
            distance.x = 0;
        }
        if (isAxisFixed(YAXIS)) {
            distance.y = 0;
        }
        if (isAxisFixed(ZAXIS)) {
            distance.z = 0;
        }

        if (setEndCoords(endCoords + distance).getResult())
        r = setStartCoords(startCoords + distance);
        referenceCoords = coords;
        r.setMessage("Box translated");

    }

    modifyActiveSelection();
    return r;
}



void SelectionTool::execute(){
    if (action == ADD || action == SUBTRACT) {
        prevState = global->getSelection();

    }
    else if (action == EDIT_SUBTRACT || action == EDIT_ADD) {

        for (int i = prevStart.x; i<prevEnd.x; i++)
            for(int j = prevStart.y; j<prevEnd.y; j++)
                for (int k = prevStart.z; k<prevEnd.z; k++) {
                    if(action == EDIT_SUBTRACT) {
                        if (prevState.find(vector3<int>(i, j, k))!=prevState.end())
                            global->addVoxelToSelection(i,j, k);
                    }
                    else {
                        if (prevState.find(vector3<int>(i, j, k))==prevState.end()) {
                            global->deleteVoxelFromSelection(i, j, k);
                        }
                    }
                }
    }

    for (int i = startCoords.x; i<endCoords.x; i++)
        for (int j = startCoords.y; j<endCoords.y; j++)
            for (int k = startCoords.z; k<endCoords.z; k++){
                if (action == ADD || action == EDIT_ADD) {
                    global->addVoxelToSelection(i, j, k);
                }
                else {
                    global->deleteVoxelFromSelection(i, j, k);
                }
            }


    prevStart = startCoords;
    prevEnd = endCoords;
}




void SelectionTool::modifyActiveSelection() {
    if (action==EDIT_ADD || action == EDIT_SUBTRACT) {
        execute();
    }
    else if (action==ADD) {
        startCoords = referenceCoords - vector3<int>(DEFAULT_DISTANCE, DEFAULT_DISTANCE, DEFAULT_DISTANCE);
        endCoords = referenceCoords + vector3<int>(DEFAULT_DISTANCE, DEFAULT_DISTANCE, DEFAULT_DISTANCE);
        execute();
        action=EDIT_ADD;
    }
    else if (action==SUBTRACT) {
        startCoords = referenceCoords - vector3<int>(DEFAULT_DISTANCE, DEFAULT_DISTANCE, DEFAULT_DISTANCE);
        endCoords = referenceCoords + vector3<int>(DEFAULT_DISTANCE, DEFAULT_DISTANCE, DEFAULT_DISTANCE);
        execute();
        action = EDIT_SUBTRACT;
    }

    showBox = true;
}











void SelectionTool::display() {
    if (showBox) {
        if (action==ADD || action==EDIT_ADD)
            Drawing::drawWireCuboid(startCoords, endCoords, ADD_R, ADD_G, ADD_B);
        else
            Drawing::drawWireCuboid(startCoords, endCoords, ERASE_R, ERASE_G, ERASE_B);
    }
}







Result SelectionTool::keyPressEvent(QKeyEvent *event){
    int key = event->key();
    switch(key){
    case(KEY_FIX_X):
        setAxis(XAXIS, true);
        break;
    case (KEY_FIX_Y):
        setAxis(YAXIS, true);
        break;
    case (KEY_FIX_Z):
        setAxis(ZAXIS, true);
        break;
    case (KEY_RESET):
    {
        global->clearSelection();
        prevState.clear();
        action = ADD;
        showBox = false;
        break;
    }

    case (KEY_LEFT):
    {
        startCoords.x--;
        endCoords.x--;
        modifyActiveSelection();
        break;
    }
    case (KEY_RIGHT):
    {
        startCoords.x++;
        endCoords.x++;
        modifyActiveSelection();
        break;
    }
    case (KEY_DOWN):
    {
        startCoords.y--;
        endCoords.y--;
        modifyActiveSelection();
        break;
    }
    case (KEY_UP):
    {
        startCoords.y++;
        endCoords.y++;
        modifyActiveSelection();
        break;
    }
    case (KEY_OUT):
    {
        startCoords.z++;
        endCoords.z++;
        modifyActiveSelection();
        break;
    }
    case (KEY_IN):
    {
        startCoords.z--;
        endCoords.z--;
        modifyActiveSelection();
        break;
    }

    }
    return Result(true);
}







Result SelectionTool::keyReleaseEvent(QKeyEvent *event) {
    int key = event->key();
    if (!event->isAutoRepeat()) {
        switch(key){
        case(KEY_FIX_X):
            setAxis(XAXIS, false);
            break;
        case (KEY_FIX_Y):
            setAxis(YAXIS, false);
            break;
        case (KEY_FIX_Z):
            setAxis(ZAXIS, false);
            break;
        }

        if (key==KEY_LEFT || key==KEY_RIGHT || key==KEY_UP || key==KEY_DOWN || key==KEY_IN || key==KEY_OUT) {
            modifyActiveSelection();
        }
    }
    return Result(false, "Unrecognised key");
}







Result SelectionTool::mousePressEvent(QMouseEvent *event){

    if (event->button()==Qt::LeftButton) {
        vector3<int> coords = REND->mouseUtils.getMouseOverVoxelPt(event->x(), event->y());

        referenceCoords = coords;

        if ((event->modifiers() & KEY_ADD)) {
            action = ADD;
        }
        else if (event->modifiers() & KEY_SUBTRACT){
            action = SUBTRACT;
        }

        if ((event->modifiers() & KEY_TRANSLATE)) {
            mode = TRANSLATE;
        }
        else {
            mode = SCALE;
        }
        return operationHandler(coords);
    }
    return Result(false);

}





Result SelectionTool::mouseMoveEvent(QMouseEvent *event){
    if (event->buttons() & Qt::LeftButton) {
        vector3<int> coords = REND->mouseUtils.getMouseOverVoxelPt(event->x(), event->y());
        return operationHandler(coords);
    }
    return Result(false, "No mouse move action taken");
}







Result SelectionTool::mouseReleaseEvent(QMouseEvent *event){
    if (event->buttons()&Qt::LeftButton) {
        mode = SCALE;
        return Result(true, "Scale mode activated");
    }
    return Result(true);
}
